"""
HSP Symmetry API 路由
提供点群、空间群、磁性空间群、自旋空间群的查询接口

使用 Beanie ODM 进行异步数据库操作
"""

from functools import lru_cache
from typing import Annotated

from fastapi import APIRouter, HTTPException, Query

from app.models import (
    CrystallographicPointGroup,
    MagneticSpaceGroupBase,
    MagneticSpaceGroupDetail,
    MagneticSpaceGroupDocument,
    NonCrystallographicPointGroup,
    PaginatedResponse,
    PointGroupDocument,
    PointGroupTree,
    SpaceGroupBase,
    SpaceGroupDetail,
    SpaceGroupDocument,
    SpinSpaceGroupBase,
    SpinSpaceGroupDetail,
    SpinSpaceGroupDocument,
)

router = APIRouter(prefix="/symmetry", tags=["Symmetry"])


# ==================== 点群端点 ====================


@router.get(
    "/point-groups",
    summary="获取所有点群列表",
    response_model=PaginatedResponse[CrystallographicPointGroup | NonCrystallographicPointGroup],
)
async def get_point_groups():
    """
    获取所有点群列表（晶体点群 + 非晶体点群）
    前端自行进行筛选和过滤

    排序规则：
    1. 晶体点群在前（按 order 数字升序）
    2. 非晶点群在后（按 family 和 order 表达式排序）
    """
    all_groups = await PointGroupDocument.find().to_list()

    return {"data": all_groups, "total": len(all_groups)}


@router.get("/point-groups/{group_id}", summary="获取点群详细信息")
async def get_point_group_detail(group_id: str):
    """
    获取指定点群的详细信息，包括：
    - 生成元
    - 对称操作
    - 不可约表示
    - 特征标表
    - 子群/超群关系
    """
    group = await PointGroupDocument.find_one({"group_id": group_id})

    if not group:
        raise HTTPException(status_code=404, detail=f"点群 {group_id} 未找到")

    return group


@router.get("/point-groups-tree", summary="获取点群关系树数据", response_model=PointGroupTree)
async def get_point_groups_tree():
    """
    获取所有点群的群-子群关系数据，用于绘制关系图
    返回节点和边的数据结构
    """
    crystallographic_groups = await PointGroupDocument.find(
        PointGroupDocument.is_crystallographic == True  # noqa: E712
    ).to_list()
    adjacency_map = {g.group_id: g.subgroups for g in crystallographic_groups}

    @lru_cache(maxsize=None)
    def descendants(node: str):
        reachable: set[str] = set()
        for subgroup in adjacency_map.get(node, []):
            child = subgroup.schoenflies
            reachable.add(child)
            reachable.update(descendants(child))
        return frozenset(reachable)

    reduced_edges: list[dict[str, str | int]] = []
    for src, subgroups in adjacency_map.items():
        for subgroup in subgroups:
            tgt = subgroup.schoenflies
            if not any(tgt in descendants(other.schoenflies) for other in subgroups if other.schoenflies != tgt):
                reduced_edges.append({"source": src, "target": tgt, "index": subgroup.index})

    return {"nodes": crystallographic_groups, "edges": reduced_edges}


# ==================== 空间群端点 ====================


@router.get("/space-groups", summary="获取空间群列表", response_model=PaginatedResponse[SpaceGroupBase])
async def get_space_groups(
    number: Annotated[int | None, Query(ge=1, le=230)] = None,
    crystal_system: Annotated[str | None, Query()] = None,
    point_group: Annotated[str | None, Query()] = None,
    setting: Annotated[str | None, Query()] = None,
    page: Annotated[int, Query(ge=1)] = 1,
    page_size: Annotated[int, Query(ge=1, le=100)] = 20,
):
    """
    获取晶体空间群列表（230个），支持筛选：
    - number: 空间群编号
    - crystal_system: 晶系
    - point_group: 对应点群
    - setting: 设置（如 hexagonal, rhombohedral）
    """
    # 构建 Beanie 查询
    query = SpaceGroupDocument.find()

    if number is not None:
        query = query.find(SpaceGroupDocument.number == number)
    if crystal_system:
        query = query.find(SpaceGroupDocument.crystal_system == crystal_system)
    if point_group:
        query = query.find(SpaceGroupDocument.point_group == point_group)
    if setting:
        query = query.find(SpaceGroupDocument.setting == setting)

    # 获取总数
    total = await query.count()

    # 分页查询
    skip = (page - 1) * page_size
    groups = await query.skip(skip).limit(page_size).to_list()

    # 转换为响应模型
    results = [group.model_dump(exclude={"id", "revision_id"}) for group in groups]

    return {"total": total, "data": results}


@router.get("/space-groups/{group_id}", summary="获取空间群详细信息")
async def get_space_group_detail(group_id: str) -> SpaceGroupDetail:
    """
    获取指定空间群的详细信息，包括：
    - Wyckoff 位置
    - 生成元
    - 不可约表示
    - 子群/超群关系
    """
    # 使用 Beanie 查询
    group = await SpaceGroupDocument.find_one(SpaceGroupDocument.group_id == group_id)

    if not group:
        raise HTTPException(status_code=404, detail=f"空间群 {group_id} 未找到")

    return SpaceGroupDetail(**group.model_dump(exclude={"id", "revision_id"}))


@router.get("/space-groups/{group_id}/representations", summary="获取空间群表示", response_model=PaginatedResponse)
async def get_space_group_representations(
    group_id: str,
    page: Annotated[int, Query(ge=1)] = 1,
    page_size: Annotated[int, Query(ge=1, le=100)] = 20,
):
    """获取指定空间群在k点处的不可约表示（分页）"""
    # 使用 Beanie 查询
    group = await SpaceGroupDocument.find_one(SpaceGroupDocument.group_id == group_id)

    if not group:
        raise HTTPException(status_code=404, detail=f"空间群 {group_id} 未找到")

    representations = group.representations
    total = len(representations)
    start = (page - 1) * page_size
    end = start + page_size

    # 转换为字典列表
    rep_dicts = [rep.model_dump() for rep in representations[start:end]]

    return {"total": total, "data": rep_dicts}


@router.get("/space-groups/{group_id}/subgroups", summary="获取空间群子群关系")
async def get_space_group_subgroups(group_id: str):
    """获取指定空间群的子群关系树"""
    # 使用 Beanie 查询
    group = await SpaceGroupDocument.find_one(SpaceGroupDocument.group_id == group_id)

    if not group:
        raise HTTPException(status_code=404, detail=f"空间群 {group_id} 未找到")

    return {"group_id": group_id, "subgroups": group.subgroups}


# ==================== 磁性空间群端点 ====================


@router.get("/magnetic-groups", summary="获取磁性空间群列表", response_model=PaginatedResponse[MagneticSpaceGroupBase])
async def get_magnetic_groups(
    og_number: Annotated[int | None, Query(ge=1, le=1651)] = None,
    og_type: Annotated[str | None, Query()] = None,
    parent_space_group: Annotated[int | None, Query()] = None,
    page: Annotated[int, Query(ge=1)] = 1,
    page_size: Annotated[int, Query(ge=1, le=100)] = 20,
):
    """
    获取磁性空间群列表（1651个），支持筛选：
    - og_number: OG编号
    - og_type: OG类型（Type-I, Type-II, Type-III, Type-IV）
    - parent_space_group: 母空间群编号
    """
    # 构建 Beanie 查询
    query = MagneticSpaceGroupDocument.find()

    if og_number is not None:
        query = query.find(MagneticSpaceGroupDocument.og_number == og_number)
    if og_type:
        query = query.find(MagneticSpaceGroupDocument.og_type == og_type)
    if parent_space_group is not None:
        query = query.find(MagneticSpaceGroupDocument.parent_space_group == parent_space_group)

    # 获取总数
    total = await query.count()

    # 分页查询
    skip = (page - 1) * page_size
    groups = await query.skip(skip).limit(page_size).to_list()

    # 转换为响应模型
    results = [group.model_dump(exclude={"id", "revision_id"}) for group in groups]

    return {"total": total, "data": results}


@router.get("/magnetic-groups/{group_id}", summary="获取磁性空间群详细信息")
async def get_magnetic_group_detail(group_id: str) -> MagneticSpaceGroupDetail:
    """
    获取指定磁性空间群的详细信息，包括：
    - 幺正和反幺正对称操作
    - 磁性表示
    - 磁性点群
    """
    # 使用 Beanie 查询
    group = await MagneticSpaceGroupDocument.find_one(MagneticSpaceGroupDocument.group_id == group_id)

    if not group:
        raise HTTPException(status_code=404, detail=f"磁性空间群 {group_id} 未找到")

    return MagneticSpaceGroupDetail(**group.model_dump(exclude={"id", "revision_id"}))


@router.get(
    "/magnetic-groups/{group_id}/representations", summary="获取磁性空间群表示", response_model=PaginatedResponse
)
async def get_magnetic_group_representations(
    group_id: str,
    page: Annotated[int, Query(ge=1)] = 1,
    page_size: Annotated[int, Query(ge=1, le=100)] = 20,
):
    """获取指定磁性空间群的磁性表示（分页）"""
    # 使用 Beanie 查询
    group = await MagneticSpaceGroupDocument.find_one(MagneticSpaceGroupDocument.group_id == group_id)

    if not group:
        raise HTTPException(status_code=404, detail=f"磁性空间群 {group_id} 未找到")

    representations = group.representations
    total = len(representations)
    start = (page - 1) * page_size
    end = start + page_size

    # 转换为字典列表
    rep_dicts = [rep.model_dump() for rep in representations[start:end]]

    return {"total": total, "data": rep_dicts}


# ==================== 自旋空间群端点 ====================


@router.get("/spin-groups", summary="获取自旋空间群列表", response_model=PaginatedResponse[SpinSpaceGroupBase])
async def get_spin_groups(
    ssg_number: Annotated[int | None, Query()] = None,
    parent_msg: Annotated[int | None, Query()] = None,
    parent_space_group: Annotated[int | None, Query()] = None,
    page: Annotated[int, Query(ge=1)] = 1,
    page_size: Annotated[int, Query(ge=1, le=100)] = 20,
):
    """
    获取自旋空间群列表，支持筛选：
    - ssg_number: 自旋空间群编号
    - parent_msg: 母磁性空间群OG编号
    - parent_space_group: 母空间群编号
    """
    # 构建 Beanie 查询
    query = SpinSpaceGroupDocument.find()

    if ssg_number is not None:
        query = query.find(SpinSpaceGroupDocument.ssg_number == ssg_number)
    if parent_msg is not None:
        query = query.find(SpinSpaceGroupDocument.parent_msg == parent_msg)
    if parent_space_group is not None:
        query = query.find(SpinSpaceGroupDocument.parent_space_group == parent_space_group)

    # 获取总数
    total = await query.count()

    # 分页查询
    skip = (page - 1) * page_size
    groups = await query.skip(skip).limit(page_size).to_list()

    # 转换为响应模型
    results = [group.model_dump(exclude={"id", "revision_id"}) for group in groups]

    return {"total": total, "data": results}


@router.get("/spin-groups/{group_id}", summary="获取自旋空间群详细信息")
async def get_spin_group_detail(group_id: str) -> SpinSpaceGroupDetail:
    """
    获取指定自旋空间群的详细信息，包括：
    - 自旋对称操作
    - 自旋表示
    - 自旋点群
    """
    # 使用 Beanie 查询
    group = await SpinSpaceGroupDocument.find_one(SpinSpaceGroupDocument.group_id == group_id)

    if not group:
        raise HTTPException(status_code=404, detail=f"自旋空间群 {group_id} 未找到")

    return SpinSpaceGroupDetail(**group.model_dump(exclude={"id", "revision_id"}))


@router.get("/spin-groups/{group_id}/representations", summary="获取自旋空间群表示", response_model=PaginatedResponse)
async def get_spin_group_representations(
    group_id: str,
    page: Annotated[int, Query(ge=1)] = 1,
    page_size: Annotated[int, Query(ge=1, le=100)] = 20,
):
    """获取指定自旋空间群的自旋表示（分页）"""
    # 使用 Beanie 查询
    group = await SpinSpaceGroupDocument.find_one(SpinSpaceGroupDocument.group_id == group_id)

    if not group:
        raise HTTPException(status_code=404, detail=f"自旋空间群 {group_id} 未找到")

    representations = group.representations
    total = len(representations)
    start = (page - 1) * page_size
    end = start + page_size

    # 转换为字典列表
    rep_dicts = [rep.model_dump() for rep in representations[start:end]]

    return {"total": total, "data": rep_dicts}


# ==================== 统计端点 ====================


@router.get("/statistics", summary="获取数据库统计信息")
async def get_statistics():
    """获取 HSP 数据库的统计信息"""
    # 使用 Beanie 异步查询统计数据
    stats = {
        "point_groups": {
            "total": await PointGroupDocument.count(),
            "crystallographic": await PointGroupDocument.find(
                PointGroupDocument.is_crystallographic == True  # noqa: E712
            ).count(),
            "non_crystallographic": await PointGroupDocument.find(
                PointGroupDocument.is_crystallographic == False  # noqa: E712
            ).count(),
        },
        "space_groups": {"total": await SpaceGroupDocument.count()},
        "magnetic_space_groups": {
            "total": await MagneticSpaceGroupDocument.count(),
            "type_i": await MagneticSpaceGroupDocument.find(MagneticSpaceGroupDocument.og_type == "Type-I").count(),
            "type_ii": await MagneticSpaceGroupDocument.find(MagneticSpaceGroupDocument.og_type == "Type-II").count(),
            "type_iii": await MagneticSpaceGroupDocument.find(MagneticSpaceGroupDocument.og_type == "Type-III").count(),
            "type_iv": await MagneticSpaceGroupDocument.find(MagneticSpaceGroupDocument.og_type == "Type-IV").count(),
        },
        "spin_space_groups": {"total": await SpinSpaceGroupDocument.count()},
    }

    return stats
